package cn.st4rlight.util.collection;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.LongPredicate;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import lombok.AccessLevel;
import lombok.NoArgsConstructor;

/**
 * @author st4rlight <st4rlight@163.com>
 * Created on 2024-04-14
 */
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class TransformUtil {

    /**
     * toList
     */
    public static <T> List<T> toList(Collection<T> originCollection) {
        return StreamUtil.safeStream(originCollection).collect(Collectors.toList());
    }

    public static <T, R> List<R> toList(T[] originArray, Function<T, R> mapFunction) {
        return StreamUtil.safeStream(originArray).map(mapFunction).collect(Collectors.toList());
    }

    public static <T, R> List<R> toList(Collection<T> originCollection, Function<T, R> mapFunction) {
        return StreamUtil.safeStream(originCollection).map(mapFunction).collect(Collectors.toList());
    }

    /**
     * toSet
     */
    public static <T> Set<T> toSet(Collection<T> originCollection) {
        return StreamUtil.safeStream(originCollection).collect(Collectors.toSet());
    }

    public static <T, R> Set<R> toSet(T[] originArray, Function<T, R> mapFunction) {
        return StreamUtil.safeStream(originArray).map(mapFunction).collect(Collectors.toSet());
    }

    public static <T, R> Set<R> toSet(List<T> originCollection, Function<T, R> mapFunction) {
        return StreamUtil.safeStream(originCollection).map(mapFunction).collect(Collectors.toSet());
    }

    /**
     * toMap
     */
    public static <T, K> Map<K, T> toMap(T[] originArray, Function<T, K> keyMapper) {
        return toMap(toList(originArray, Function.identity()), keyMapper, Function.identity());
    }

    public static <T, K> Map<K, T> toMap(Collection<T> originCollection, Function<T, K> keyMapper) {
        return toMap(originCollection, keyMapper, Function.identity());
    }

    public static <T, K, V> Map<K, V> toMap(T[] originArray, Function<T, K> keyMapper, Function<T, V> valueMapper) {
        if (ArrayUtils.isEmpty(originArray)) {
            return Maps.newHashMap();
        }
        return toMap(Lists.newArrayList(originArray), keyMapper, valueMapper);
    }

    public static <T, K, V> Map<K, V> toMap(Collection<T> originCollection,
            Function<T, K> keyMapper, Function<T, V> valueMapper) {
        if (CollectionUtils.isEmpty(originCollection)) {
            return Maps.newHashMap();
        }
        return StreamUtil.safeStream(originCollection).collect(HashMap::new,
            (map, item) -> map.put(keyMapper.apply(item), valueMapper.apply(item)),
            HashMap::putAll
        );
    }

    /**
     * entry列表转map
     */
    public static <K, V> Map<K, V> entries2Map(Collection<Map.Entry<K, V>> entryCollection) {
        return toMap(entryCollection, Map.Entry::getKey, Map.Entry::getValue);
    }

    /**
     * groupBy2List
     */
    public static <T, K> Map<K, List<T>> groupBy2List(Collection<T> originCollection, Function<T, K> keyMapper) {
        return StreamUtil.safeStream(originCollection).collect(Collectors.groupingBy(keyMapper));
    }

    public static <T, K, V> Map<K, List<V>> groupBy2List(
        Collection<T> originCollection, Function<T, K> keyMapper, Function<T, V> valueMapper
    ) {
        // 第一层map转换
        Map<K, List<T>> tempMap = groupBy2List(originCollection, keyMapper);
        // 第二层转换
        Map<K, List<V>> resultMap = Maps.newHashMap();
        tempMap.forEach((key, value) ->
            resultMap.put(key, toList(value, valueMapper))
        );

        return resultMap;
    }

    public static <T, K1, K2> Map<K1, Map<K2, List<T>>> doubleGroupBy(
        Collection<T> originCollection, Function<T, K1> key1Mapper, Function<T, K2> key2Mapper
    ) {
        if (CollectionUtils.isEmpty(originCollection)) {
            return Maps.newHashMap();
        }

        return StreamUtil.safeStream(originCollection).collect(
                Collectors.groupingBy(key1Mapper, Collectors.groupingBy(key2Mapper)));
    }

    public static <T, K1, K2, V> Map<K1, Map<K2, List<V>>> doubleGroupBy(
        Collection<T> originCollection, Function<T, K1> key1Mapper,
        Function<T, K2> key2Mapper, Function<T, V> valueMapper
    ) {
        // 第一层map转换
        Map<K1, List<T>> tempMap = groupBy2List(originCollection, key1Mapper);
        // 第二层转换
        Map<K1, Map<K2, List<V>>> resultMap = Maps.newHashMap();
        tempMap.forEach((key, value) ->
            resultMap.put(key, groupBy2List(value, key2Mapper, valueMapper))
        );

        return resultMap;
    }

    /**
     * distinct
     */
    public static <T, R> Predicate<T> distinctByKey(Function<? super T, R> keyMapper) {
        Set<R> everSeen = ConcurrentHashMap.newKeySet();
        return t -> everSeen.add(keyMapper.apply(t));
    }

    public static <T> List<T> distinctList(List<T> originList) {
        return StreamUtil.safeStream(originList).distinct().collect(Collectors.toList());
    }

    public static LongPredicate distinctByLong() {
        Set<Long> everSeen = ConcurrentHashMap.newKeySet();
        return everSeen::add;
    }

    public static <T> Predicate<T> distinctByIdentity() {
        Set<T> everSeen = ConcurrentHashMap.newKeySet();
        return everSeen::add;
    }
}
